/*
 * Created on 11/22/2012 - 3:31 PM
 */
package sembr.vtk.data.input;

import vtk.*;

/**
 *
 * @author adriano
 */
public class PCF3 extends SourceBase
{
    double[][] pointsToSetCore;
    private vtkImplicitBoolean hollowCore;
    private vtkImplicitBoolean modeledCylinder;
    private vtkCylinder core;
    private vtkCylinder mainCylinder;
    private vtkPlane vertPlane;
    private vtkPlane basePlane;
    private vtkImplicitBoolean bigCylinder;
    private vtkImplicitBoolean tubes;
    private vtkImplicitBoolean device;
    private vtkSampleFunction deviceSample;
    private vtkSampleFunction coreSample;
    private vtkContourFilter deviceSurface;
    private vtkContourFilter coreSurface;
    private vtkDelaunay3D delaunay3D;
                           
    public PCF3(double[] center, 
                            double radius, 
                            double[] plane1Origin, 
                            double[] plane1Normal,
                            double[] plane2Origin,                             
                            double[] plane2Normal
                           )
    {
        setMainCylinder(center, radius);
        setCylinderHeightByPlanes(plane1Origin, plane1Normal, plane2Origin, plane2Normal);
        setBooleanOfBigCylinder('D');
        setBigCylinderFunctions();
        setTubes();
        setCore();
        setCoreSampleFunction();
        setDevice();
        setDeviceSampleFunction();
        setDeviceSurface();
        //setCoreSurface();
        //applyDelaunay3DTriangularization();
        //setTubesDieletric(1.0);
        //setCylinderDieletric(12.0);
        //appendTubesWithCylinder();
        //setDeviceScalar();            
    }
    
    private void setMainCylinder(double[] center, double radius)
    {
        mainCylinder = new vtkCylinder();
        mainCylinder.SetCenter(center);
        mainCylinder.SetRadius(radius);
    }
    
    private void setCylinderHeightByPlanes(double[] plane1Origin, 
                                           double[] plane1Normal,
                                           double[] plane2Origin,                             
                                           double[] plane2Normal)
    {
        vertPlane = new vtkPlane();
        vertPlane.SetOrigin(plane1Origin);
        vertPlane.SetNormal(plane1Normal);
                
        basePlane = new vtkPlane();
        basePlane.SetOrigin(plane2Origin);
        basePlane.SetNormal(plane2Normal);         
    }
    
    private void setBooleanOfBigCylinder(char operation)
    {
        bigCylinder = new vtkImplicitBoolean();
        
        switch (operation)
        {
            // union
            case 'U':
                bigCylinder.SetOperationTypeToUnion();
                break;
            // difference    
            case 'D':
                bigCylinder.SetOperationTypeToDifference();
                break;
            // intersection
            case 'I':
                bigCylinder.SetOperationTypeToIntersection();
                break;
            // union of magnitudes    
            case 'M':
                bigCylinder.SetOperationTypeToUnionOfMagnitudes();
                break;
            default:
                bigCylinder.SetOperationTypeToUnion();
                break;
        }       
    }
    
    private void setBigCylinderFunctions()
    {
        bigCylinder.AddFunction(mainCylinder);
    }   
    
    private void setTubes()
    {        
        double xShift;  
        double zShift;
        vtkCylinder xMirrorCurrentCylinder;       
        vtkCylinder yMirrorCurrentCylinder;
        vtkCylinder xyMirrorCurrentCylinder;
        // seventeen tubes fill the cylinder diameter queued side by side
        double tubesDiameter = (mainCylinder.GetRadius() * 2) / 15;
        double widthBetweenTubes = tubesDiameter / 3;
        double[] origin = mainCylinder.GetCenter();
                       
       vtkCylinder currentCylinder;
        //currentCylinder.SetCenter(origin);
        // central tube with bigger radius
        //currentCylinder.SetRadius(tubesDiameter);
        
        tubes = new vtkImplicitBoolean();
        tubes.SetOperationTypeToUnion();
        //tubes.AddFunction(currentCylinder);
                
        pointsToSetCore = new double[12][3];
        
        hollowCore = new vtkImplicitBoolean();
        hollowCore.SetOperationTypeToUnion();
        double tubesRadius = (mainCylinder.GetRadius()) / 15;
        double widthBetweenHollowCore = tubesRadius / 2;
        double radioHollowCore = tubesRadius + widthBetweenHollowCore;
        
        int i;
        // for central tubes
        for (i = 2; i < 6; i++)
        {            
            // determine center
            xShift = origin[0] + i * (widthBetweenTubes + tubesDiameter);
            
            currentCylinder = new vtkCylinder();
            xMirrorCurrentCylinder = new vtkCylinder();
                       
            currentCylinder.SetCenter(xShift, origin[1], origin[2]);
            currentCylinder.SetRadius(tubesDiameter / 2);            
            
            xMirrorCurrentCylinder.SetCenter(-xShift, origin[1], origin[2]);
            xMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
            
            tubes.AddFunction(currentCylinder);
            tubes.AddFunction(xMirrorCurrentCylinder);
            
            if (i == 2) 
            {
                //xShift = xShift - 0.06;
                pointsToSetCore[0] = new double[]{xShift, origin[1], origin[2]}; 
                vtkCylinder currentCylinder2 = new vtkCylinder();
                currentCylinder2.SetCenter(pointsToSetCore[0]);
                currentCylinder2.SetRadius(radioHollowCore);
                
                pointsToSetCore[1] = new double[]{-xShift, origin[1], origin[2]};
                vtkCylinder xMirrorCurrentCylinder2 = new vtkCylinder();
                xMirrorCurrentCylinder2.SetCenter(pointsToSetCore[1]);
                xMirrorCurrentCylinder2.SetRadius(radioHollowCore);
                
                hollowCore.AddFunction(currentCylinder2);
                hollowCore.AddFunction(xMirrorCurrentCylinder2);
            }
        }
        
        // middle tubes displacement in z coordinate
        zShift = origin[2] + tubesDiameter;
        
        for (i = 1; i < 5; i++) 
        {           
            xShift = widthBetweenTubes * 2 + origin[0] + i * (widthBetweenTubes + tubesDiameter);
                              
            currentCylinder = new vtkCylinder();
            xMirrorCurrentCylinder = new vtkCylinder();
            yMirrorCurrentCylinder = new vtkCylinder();
            xyMirrorCurrentCylinder = new vtkCylinder();
                        
            currentCylinder.SetCenter(xShift, origin[1], zShift);
            currentCylinder.SetRadius(tubesDiameter / 2);
                        
            xMirrorCurrentCylinder.SetCenter(-xShift, origin[1], zShift);
            xMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            yMirrorCurrentCylinder.SetCenter(xShift, origin[1], -zShift);
            yMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            xyMirrorCurrentCylinder.SetCenter(-xShift, origin[1], -zShift);
            xyMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
            
            tubes.AddFunction(currentCylinder);
            tubes.AddFunction(xMirrorCurrentCylinder);
            tubes.AddFunction(yMirrorCurrentCylinder);
            tubes.AddFunction(xyMirrorCurrentCylinder);  
            
            if (i == 1) 
            {
                //xShift = xShift - 0.06;
                pointsToSetCore[2] = new double[]{xShift, origin[1], zShift}; 
                vtkCylinder currentCylinder2 = new vtkCylinder();
                currentCylinder2.SetCenter(pointsToSetCore[2]);
                currentCylinder2.SetRadius(radioHollowCore);
               
                pointsToSetCore[3] = new double[]{-xShift, origin[1], zShift};
                vtkCylinder xMirrorCurrentCylinder2 = new vtkCylinder();
                xMirrorCurrentCylinder2.SetCenter(pointsToSetCore[3]);
                xMirrorCurrentCylinder2.SetRadius(radioHollowCore);
                
                pointsToSetCore[4] = new double[]{xShift, origin[1], -zShift};
                vtkCylinder yMirrorCurrentCylinder2 = new vtkCylinder();
                yMirrorCurrentCylinder2.SetCenter(pointsToSetCore[4]);
                yMirrorCurrentCylinder2.SetRadius(radioHollowCore);
                
                pointsToSetCore[5] = new double[]{-xShift, origin[1], -zShift};
                vtkCylinder xyMirrorCurrentCylinder2 = new vtkCylinder();
                xyMirrorCurrentCylinder2.SetCenter(pointsToSetCore[5]);
                xyMirrorCurrentCylinder2.SetRadius(radioHollowCore);
                
                hollowCore.AddFunction(currentCylinder2);
                hollowCore.AddFunction(xMirrorCurrentCylinder2);
                hollowCore.AddFunction(yMirrorCurrentCylinder2);
                hollowCore.AddFunction(xyMirrorCurrentCylinder2);  
            }
        }
      
        // second middle tubes displacement in y coodinate 
        zShift = origin[2] + 2 * tubesDiameter;
        
        for (i = 0; i <= 4; i++)
        {
            xShift = origin[0] + i * (widthBetweenTubes + tubesDiameter);
            
            currentCylinder = new vtkCylinder();
            xMirrorCurrentCylinder = new vtkCylinder();
            yMirrorCurrentCylinder = new vtkCylinder();
            xyMirrorCurrentCylinder = new vtkCylinder();
                        
            currentCylinder.SetCenter(xShift, origin[1], zShift);
            currentCylinder.SetRadius(tubesDiameter / 2);
                       
            xMirrorCurrentCylinder.SetCenter(-xShift, origin[1], zShift);
            xMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                       
            yMirrorCurrentCylinder.SetCenter(xShift, origin[1], -zShift);
            yMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            xyMirrorCurrentCylinder.SetCenter(-xShift, origin[1], -zShift);
            xyMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                          
            tubes.AddFunction(currentCylinder);
            tubes.AddFunction(xMirrorCurrentCylinder);
            tubes.AddFunction(yMirrorCurrentCylinder);
            tubes.AddFunction(xyMirrorCurrentCylinder);    
            
            if (i == 0) 
            {
                pointsToSetCore[6] = new double[]{xShift, origin[1], zShift};
                vtkCylinder currentCylinder2 = new vtkCylinder();
                currentCylinder2.SetCenter(pointsToSetCore[6]);
                currentCylinder2.SetRadius(radioHollowCore);
                
                pointsToSetCore[7] = new double[]{xShift, origin[1], -zShift};
                vtkCylinder yMirrorCurrentCylinder2 = new vtkCylinder();
                yMirrorCurrentCylinder2.SetCenter(pointsToSetCore[7]);
                yMirrorCurrentCylinder2.SetRadius(radioHollowCore);
                
                hollowCore.AddFunction(currentCylinder2);
                hollowCore.AddFunction(yMirrorCurrentCylinder2);
            }
            
            if (i == 1)
            {
                //xShift = xShift - 0.06;
                pointsToSetCore[8] = new double[]{xShift, origin[1], zShift}; 
                vtkCylinder currentCylinder2 = new vtkCylinder();
                currentCylinder2.SetCenter(pointsToSetCore[8]);
                currentCylinder2.SetRadius(radioHollowCore);
                
                pointsToSetCore[9] = new double[]{-xShift, origin[1], zShift};
                vtkCylinder xMirrorCurrentCylinder2 = new vtkCylinder();
                xMirrorCurrentCylinder2.SetCenter(pointsToSetCore[9]);
                xMirrorCurrentCylinder2.SetRadius(radioHollowCore);
                
                pointsToSetCore[10] = new double[]{xShift, origin[1], -zShift};
                vtkCylinder yMirrorCurrentCylinder2 = new vtkCylinder();
                yMirrorCurrentCylinder2.SetCenter(pointsToSetCore[10]);
                yMirrorCurrentCylinder2.SetRadius(radioHollowCore);
                
                pointsToSetCore[11] = new double[]{-xShift, origin[1], -zShift};
                vtkCylinder xyMirrorCurrentCylinder2 = new vtkCylinder();                        
                xyMirrorCurrentCylinder2.SetCenter(pointsToSetCore[11]);
                xyMirrorCurrentCylinder2.SetRadius(radioHollowCore);
                
                hollowCore.AddFunction(currentCylinder2);
                hollowCore.AddFunction(xMirrorCurrentCylinder2);
                hollowCore.AddFunction(yMirrorCurrentCylinder2);
                hollowCore.AddFunction(xyMirrorCurrentCylinder2); 
            }
        }
        
        // second middle tubes displacement in y coodinate 
        zShift = origin[2] + 3 * tubesDiameter;
        
        for (i = 0; i <= 3; i++)
        {            
            xShift = widthBetweenTubes * 2 + origin[0] + i * (widthBetweenTubes + tubesDiameter);            
            
            currentCylinder = new vtkCylinder();
            xMirrorCurrentCylinder = new vtkCylinder();
            yMirrorCurrentCylinder = new vtkCylinder();
            xyMirrorCurrentCylinder = new vtkCylinder();
            
            currentCylinder.SetCenter(xShift, origin[1], zShift);
            currentCylinder.SetRadius(tubesDiameter / 2);
                       
            xMirrorCurrentCylinder.SetCenter(-xShift, origin[1], zShift);
            xMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            yMirrorCurrentCylinder.SetCenter(xShift, origin[1], -zShift);
            yMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            xyMirrorCurrentCylinder.SetCenter(-xShift, origin[1], -zShift);
            xyMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
            
            tubes.AddFunction(currentCylinder);
            tubes.AddFunction(xMirrorCurrentCylinder);
            tubes.AddFunction(yMirrorCurrentCylinder);
            tubes.AddFunction(xyMirrorCurrentCylinder);             
        } 
        
        // second middle tubes displacement in y coodinate 
        zShift = origin[2] + 4 * tubesDiameter;
        
        for (i = 0; i <= 3; i++)
        {
            xShift = origin[0] + i * (widthBetweenTubes + tubesDiameter);
            
            currentCylinder = new vtkCylinder();
            xMirrorCurrentCylinder = new vtkCylinder();
            yMirrorCurrentCylinder = new vtkCylinder();
            xyMirrorCurrentCylinder = new vtkCylinder();
                        
            currentCylinder.SetCenter(xShift, origin[1], zShift);
            currentCylinder.SetRadius(tubesDiameter / 2);
                       
            xMirrorCurrentCylinder.SetCenter(-xShift, origin[1], zShift);
            xMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                       
            yMirrorCurrentCylinder.SetCenter(xShift, origin[1], -zShift);
            yMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            xyMirrorCurrentCylinder.SetCenter(-xShift, origin[1], -zShift);
            xyMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                          
            tubes.AddFunction(currentCylinder);
            tubes.AddFunction(xMirrorCurrentCylinder);
            tubes.AddFunction(yMirrorCurrentCylinder);
            tubes.AddFunction(xyMirrorCurrentCylinder);                         
        } 
        
        // fifth middle tubes displacement in z coodinate 
        zShift = origin[2] + 5 * tubesDiameter;
        
        for (i = 0; i <= 2; i++)
        {            
            xShift = widthBetweenTubes * 2 + origin[0] + i * (widthBetweenTubes + tubesDiameter);            
            
            currentCylinder = new vtkCylinder();
            xMirrorCurrentCylinder = new vtkCylinder();
            yMirrorCurrentCylinder = new vtkCylinder();
            xyMirrorCurrentCylinder = new vtkCylinder();
            
            currentCylinder.SetCenter(xShift, origin[1], zShift);
            currentCylinder.SetRadius(tubesDiameter / 2);
                       
            xMirrorCurrentCylinder.SetCenter(-xShift, origin[1], zShift);
            xMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            yMirrorCurrentCylinder.SetCenter(xShift, origin[1], -zShift);
            yMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
                        
            xyMirrorCurrentCylinder.SetCenter(-xShift, origin[1], -zShift);
            xyMirrorCurrentCylinder.SetRadius(tubesDiameter / 2);
            
            tubes.AddFunction(currentCylinder);
            tubes.AddFunction(xMirrorCurrentCylinder);
            tubes.AddFunction(yMirrorCurrentCylinder);
            tubes.AddFunction(xyMirrorCurrentCylinder);  
        } 
    }
    
    private void setCore()
    {
        core = new vtkCylinder();        
        core.SetRadius(0.9);
        core.SetCenter(mainCylinder.GetCenter());        
        
        modeledCylinder = new vtkImplicitBoolean();
        modeledCylinder.SetOperationTypeToDifference();
        modeledCylinder.AddFunction(core);       
        vtkImplicitFunctionCollection hollowCoresFunction = hollowCore.GetFunction();
        
        int nrOfHollowCoreFunctions = hollowCoresFunction.GetNumberOfItems();
        
        for (int i = 0; i < nrOfHollowCoreFunctions; i++)
        {
            modeledCylinder.AddFunction((vtkImplicitFunction)hollowCoresFunction.GetItemAsObject(i));
        }    
        
        //modeledCylinder.AddFunction(vertPlane);
        //modeledCylinder.AddFunction(basePlane);
    }
    
    private void setCoreSampleFunction()
    {
        coreSample = new vtkSampleFunction();
        coreSample.SetImplicitFunction(modeledCylinder);
        coreSample.SetModelBounds(-3.5, 3.5, -3.5, 3.5, -3.5, 3.5);
        coreSample.SetSampleDimensions(150, 150, 150);
        coreSample.ComputeNormalsOff();        
    }
    
    private void setDevice()
    {
        vtkImplicitFunctionCollection bigCylinderFunctions = bigCylinder.GetFunction();
        vtkImplicitFunctionCollection tubesFunctions = tubes.GetFunction();
        //vtkImplicitFunctionCollection modeledCylinderFunctions = modeledCylinder.GetFunction();
        
        device = new vtkImplicitBoolean();
        device.SetOperationTypeToDifference();        
        
        int totalOfBigCylinderFunctions = bigCylinderFunctions.GetNumberOfItems();
        
        for (int i = 0; i < totalOfBigCylinderFunctions; i++)
        {
            device.AddFunction((vtkImplicitFunction)bigCylinderFunctions.GetItemAsObject(i));
        }
        
        int totalOfTubesFunctions = tubesFunctions.GetNumberOfItems();
        
        for (int i = 0; i < totalOfTubesFunctions; i++)
        {
            device.AddFunction((vtkImplicitFunction)tubesFunctions.GetItemAsObject(i));
        }
        
        device.AddFunction(coreSample.GetImplicitFunction());
        
//        int totalOfModeledCylinderFunctions = modeledCylinderFunctions.GetNumberOfItems();
//        
//        for (int i = 0; i < totalOfModeledCylinderFunctions; i++)
//        {
//            device.AddFunction((vtkImplicitFunction)modeledCylinderFunctions.GetItemAsObject(i));
//        }
        
        device.AddFunction(vertPlane);
        device.AddFunction(basePlane);
    }
    
    private void setDeviceSampleFunction()
    {
        deviceSample = new vtkSampleFunction();
        deviceSample.SetImplicitFunction(device);
        deviceSample.SetModelBounds(-3.5, 3.5, -3.5, 3.5, -3.5, 3.5);
        deviceSample.SetSampleDimensions(150, 150, 150);
        deviceSample.ComputeNormalsOff();
    }
    
    private void setDeviceSurface()
    {        
        deviceSurface = new vtkContourFilter();
        deviceSurface.SetInput(deviceSample.GetOutput());
        deviceSurface.SetValue(0, 0.0);
        deviceSurface.ComputeNormalsOff();
        deviceSurface.Update();
    }   
    
    private void setCoreSurface()
    {        
        coreSurface = new vtkContourFilter();
        coreSurface.SetInput(coreSample.GetOutput());
        coreSurface.SetValue(0, 0.0);
        coreSurface.ComputeNormalsOff();
        coreSurface.Update();
    }
    
    private void applyDelaunay3DTriangularization()
    {
        vtkCleanPolyData cleaner = new vtkCleanPolyData();
        cleaner.SetInputConnection(deviceSurface.GetOutputPort());
                
        delaunay3D = new vtkDelaunay3D();
        delaunay3D.SetInputConnection(cleaner.GetOutputPort());
        delaunay3D.SetAlpha(0.6);
        delaunay3D.BoundingTriangulationOn();
        //delaunay3D.SetTolerance(0.05);
        delaunay3D.Update();
    }
    
    private void setTubesDieletric(double dieletric)
    { }
    
    private void setCylinderDieletric(double dieletric)
    { }
    
    private void appendTubesWithCylinder()
    { }
    
    private void setDeviceScalar()
    { }
    
    @Override
    public vtkPolyData getPolyData()
    {
          return deviceSurface.GetOutput();
    }   
}
